﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;

namespace DataAccessLayer
{
    /// <summary>
    /// The dal entity.
    /// </summary>
    public abstract class DalEntity
    {
        
        /// <summary>
        /// Read all entities from DB.
        /// </summary>
        public abstract Reader ReadAll(Dictionary<string, object> args);

        /// <summary>
        /// Read the entity by id.
        /// </summary>
        public abstract Dictionary<string, object> ReadById(Dictionary<string, object> args); 

        /// <summary>
        /// Create an entity in DB.
        /// </summary>
        public abstract Dictionary<string, object> Create();

        /// <summary>
        /// Write the entity in DB.
        /// </summary>
        public abstract bool? Write(Dictionary<string, object> args);

        /// <summary>
        /// Remove the entity from DB.
        /// </summary>
        public abstract bool? Remove(Dictionary<string, object> args);


        protected static void ValidateArguments(Dictionary<string, object> args, string arg)
        {
                if(!args.ContainsKey(arg))
                    throw new ArgumentException("Missing field \"" + arg + "\" in arguments");
        }

        /// <summary>
        /// Read entity by id.
        /// </summary>
        protected static Dictionary<string, object> _readById(string command, List<Tuple<string, string, string, SqlDbType, int>> properties, Dictionary<string, object> args, string type)
        {
            ValidateArguments(args, "Id");

            SqlCommand cmd = new SqlCommand(command);
            
            SqlParameter id = cmd.Parameters.Add("@id", SqlDbType.Int);
            id.Direction = ParameterDirection.Input;
            id.Value = args["Id"];
            Reader reader = new Reader(cmd, properties);

            try
            {
                if (reader.Read())
                    return reader.Current;
                else
                    throw new DataException("Can't find " + type + " with Id " + id);
            }
            finally
            {
                reader.Close();
            }
        }

        protected Dictionary<string, object> _create(string command)
        {
            SqlCommand cmd = new SqlCommand(command);
            SqlParameter id = cmd.Parameters.Add("@id", SqlDbType.Int);
            id.Direction = ParameterDirection.Output;

            using (SqlConnection connection = Reader.CreateConnection(cmd))
            {
                connection.Open();
                cmd.ExecuteNonQuery();
            }

            return ReadById(new Dictionary<string, object> { { "Id", id.Value } });
        }

        protected void _write(string command, Dictionary<string, object> args, List<Tuple<string, string, string, SqlDbType, int>> properties)
        {

            SqlCommand cmd = new SqlCommand(command);

            foreach (var property in properties)
            {
                ValidateArguments(args, property.Item1);
                SqlParameter param = cmd.Parameters.Add(property.Item3, property.Item4, property.Item5);
                param.Direction = ParameterDirection.Input;
                param.Value = args[property.Item1];
            }

            using (SqlConnection connection = Reader.CreateConnection(cmd))
            {
                connection.Open();
                cmd.ExecuteNonQuery();
            }
        }

        protected void _remove(string command, Dictionary<string, object> args)
        {

            ValidateArguments(args, "Id");

            SqlCommand cmd = new SqlCommand(command);
            SqlParameter id = cmd.Parameters.Add("@id", SqlDbType.Int);
            id.Direction = ParameterDirection.Input;
            id.Value = args["Id"];

            using (SqlConnection connection = Reader.CreateConnection(cmd))
            {
                connection.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
}
